'Scan' JavaScript Asinxron Iterator yordamchisining funksionalligi, qo'llanilishi va asinxron jamlanuvchi qayta ishlash uchun afzalliklarini chuqur tahlil qilish.
JavaScript Asinxron Iterator Yordamchisi: Scan - Asinxron Akkumulyativ Qayta Ishlash
Asinxron dasturlash zamonaviy JavaScript dasturlashning asosiy tamoyillaridan biridir, ayniqsa tarmoq so'rovlari yoki fayl tizimi bilan o'zaro aloqalar kabi I/O-ga bog'liq operatsiyalar bilan ishlashda. ES2018 da taqdim etilgan asinxron iteratorlar asinxron ma'lumotlar oqimlarini boshqarish uchun kuchli mexanizmni ta'minlaydi. RxJS kabi kutubxonalarda tez-tez uchraydigan va alohida yordamchi dastur sifatida tobora ommalashib borayotgan `scan` yordamchisi ushbu asinxron ma'lumotlar oqimlarini qayta ishlash uchun yanada ko'proq imkoniyatlar ochadi.
Asinxron Iteratorlarni Tushunish
`scan` ga sho'ng'ishdan oldin, asinxron iteratorlar nima ekanligini eslab o'taylik. Asinxron iterator — bu asinxron iterator protokoliga mos keladigan obyekt. Ushbu protokol `next()` metodini belgilaydi, u ikki xususiyatga ega obyekt bilan yakunlanadigan promise qaytaradi: `value` (ketma-ketlikdagi keyingi qiymat) va `done` (iterator tugaganligini ko'rsatuvchi mantiqiy qiymat). Asinxron iteratorlar vaqt o'tishi bilan keladigan ma'lumotlar yoki olish uchun asinxron operatsiyalarni talab qiladigan ma'lumotlar bilan ishlashda ayniqsa foydalidir.
Mana asinxron iteratorning oddiy misoli:
async function* generateNumbers() {
yield 1;
yield 2;
yield 3;
}
async function main() {
const iterator = generateNumbers();
let result = await iterator.next();
console.log(result); // { value: 1, done: false }
result = await iterator.next();
console.log(result); // { value: 2, done: false }
result = await iterator.next();
console.log(result); // { value: 3, done: false }
result = await iterator.next();
console.log(result); // { value: undefined, done: true }
}
main();
`scan` Yordamchisi bilan Tanishtiruv
`scan` yordamchisi (`accumulate` yoki `reduce` deb ham ataladi) asinxron iteratorni har bir qiymatga akkumulyator funksiyasini qo'llash va jamlangan natijani chiqarish orqali o'zgartiradi. Bu massivlardagi `reduce` metodiga o'xshaydi, lekin asinxron va iteratorlar ustida ishlaydi.
Aslini olganda, `scan` asinxron iterator, akkumulyator funksiyasi va ixtiyoriy boshlang'ich qiymatni qabul qiladi. Manba iterator tomonidan chiqarilgan har bir qiymat uchun akkumulyator funksiyasi oldingi jamlangan qiymat (yoki birinchi iteratsiya bo'lsa, boshlang'ich qiymat) va iteratordagi joriy qiymat bilan chaqiriladi. Akkumulyator funksiyasining natijasi keyingi jamlangan qiymatga aylanadi, so'ngra u natijaviy asinxron iterator tomonidan chiqariladi.
Sintaksis va Parametrlar
`scan` dan foydalanishning umumiy sintaksisi quyidagicha:
async function* scan(sourceIterator, accumulator, initialValue) {
let accumulatedValue = initialValue;
for await (const value of sourceIterator) {
accumulatedValue = accumulator(accumulatedValue, value);
yield accumulatedValue;
}
}
- `sourceIterator`: O'zgartirish uchun asinxron iterator.
- `accumulator`: Ikki argument qabul qiladigan funksiya: oldingi jamlangan qiymat va iteratordagi joriy qiymat. U yangi jamlangan qiymatni qaytarishi kerak.
- `initialValue` (ixtiyoriy): Akkumulyator uchun boshlang'ich qiymat. Agar taqdim etilmasa, manba iteratordan birinchi qiymat boshlang'ich qiymat sifatida ishlatiladi va akkumulyator funksiyasi ikkinchi qiymatdan boshlab chaqiriladi.
Qo'llanilish Holatlari va Misollar
`scan` yordamchisi nihoyatda ko'p qirrali bo'lib, asinxron ma'lumotlar oqimlari bilan bog'liq turli xil stsenariylarda qo'llanilishi mumkin. Mana bir nechta misollar:
1. O'zgaruvchan Jami Summani Hisoblash
Tasavvur qiling, sizda tranzaksiya summalarini chiqaradigan asinxron iterator bor. Ushbu tranzaksiyalarning o'zgaruvchan jami summasini hisoblash uchun `scan` dan foydalanishingiz mumkin.
async function* generateTransactions() {
yield 10;
yield 20;
yield 30;
}
async function main() {
const transactions = generateTransactions();
const runningTotals = scan(transactions, (acc, value) => acc + value, 0);
for await (const total of runningTotals) {
console.log(total); // Natija: 10, 30, 60
}
}
main();
Ushbu misolda `accumulator` funksiyasi shunchaki joriy tranzaksiya summasini oldingi jami summaga qo'shadi. 0 ning `initialValue` qiymati o'zgaruvchan jami summaning noldan boshlanishini ta'minlaydi.
2. Ma'lumotlarni Massivga Jamlash
Siz asinxron iteratordan ma'lumotlarni massivga jamlash uchun `scan` dan foydalanishingiz mumkin. Bu vaqt o'tishi bilan ma'lumotlarni to'plash va ularni to'plamlarda qayta ishlash uchun foydali bo'lishi mumkin.
async function* fetchData() {
yield { id: 1, name: 'Alice' };
yield { id: 2, name: 'Bob' };
yield { id: 3, name: 'Charlie' };
}
async function main() {
const dataStream = fetchData();
const accumulatedData = scan(dataStream, (acc, value) => [...acc, value], []);
for await (const data of accumulatedData) {
console.log(data); // Natija: [{id: 1, name: 'Alice'}], [{id: 1, name: 'Alice'}, {id: 2, name: 'Bob'}], [{id: 1, name: 'Alice'}, {id: 2, name: 'Bob'}, {id: 3, name: 'Charlie'}]
}
}
main();
Bu yerda `accumulator` funksiyasi barcha oldingi elementlar va joriy qiymatni o'z ichiga olgan yangi massiv yaratish uchun spread operatoridan (`...`) foydalanadi. `initialValue` bo'sh massivdir.
3. Tezlikni Cheklovchini (Rate Limiter) Amalga Oshirish
Murakkabroq qo'llanilish holati - bu tezlikni cheklovchini amalga oshirish. Siz ma'lum bir vaqt oralig'ida qilingan so'rovlar sonini kuzatish uchun `scan` dan foydalanishingiz va agar tezlik chegarasi oshib ketgan bo'lsa, keyingi so'rovlarni kechiktirishingiz mumkin.
async function* generateRequests() {
// Kiruvchi so'rovlarni simulyatsiya qilish
yield Date.now();
await new Promise(resolve => setTimeout(resolve, 200));
yield Date.now();
await new Promise(resolve => setTimeout(resolve, 100));
yield Date.now();
}
async function main() {
const requests = generateRequests();
const rateLimitWindow = 1000; // 1 soniya
const maxRequestsPerWindow = 2;
async function* rateLimitedRequests(source, window, maxRequests) {
let queue = [];
for await (const requestTime of source) {
queue.push(requestTime);
queue = queue.filter(t => requestTime - t < window);
if (queue.length > maxRequests) {
const earliestRequest = queue[0];
const delay = window - (requestTime - earliestRequest);
console.log(`Tezlik chegarasi oshdi. ${delay}ms kechiktirilmoqda`);
await new Promise(resolve => setTimeout(resolve, delay));
}
yield requestTime;
}
}
const limited = rateLimitedRequests(requests, rateLimitWindow, maxRequestsPerWindow);
for await (const requestTime of limited) {
console.log(`So'rov ${requestTime} da qayta ishlandi`);
}
}
main();
Ushbu misol so'rov vaqt belgilarining navbatini saqlash uchun ichki ravishda (`rateLimitedRequests` funksiyasida) `scan` dan foydalanadi. U tezlikni cheklash oynasi ichidagi so'rovlar soni ruxsat etilgan maksimal miqdordan oshib ketganligini tekshiradi. Agar shunday bo'lsa, u zarur kechikishni hisoblab chiqadi va so'rovni chiqarishdan oldin pauza qiladi.
4. Real vaqtda ma'lumotlarni yig'uvchi yaratish (Global misol)
Turli birjalardan real vaqt rejimida aksiya narxlarini jamlashi kerak bo'lgan global moliyaviy dasturni ko'rib chiqing. Asinxron iterator Nyu-York fond birjasi (NYSE), London fond birjasi (LSE) va Tokio fond birjasi (TSE) kabi birjalardan narx yangilanishlarini oqim sifatida uzatishi mumkin. `scan` barcha birjalar bo'yicha ma'lum bir aksiya uchun o'zgaruvchan o'rtacha yoki yuqori/past narxni saqlab turish uchun ishlatilishi mumkin.
// Turli birjalardan oqimli aksiya narxlarini simulyatsiya qilish
async function* generateStockPrices() {
yield { exchange: 'NYSE', symbol: 'AAPL', price: 170.50 };
yield { exchange: 'LSE', symbol: 'AAPL', price: 170.75 };
await new Promise(resolve => setTimeout(resolve, 50));
yield { exchange: 'TSE', symbol: 'AAPL', price: 170.60 };
}
async function main() {
const stockPrices = generateStockPrices();
// O'zgaruvchan o'rtacha narxni hisoblash uchun scan'dan foydalanish
const runningAverages = scan(
stockPrices,
(acc, priceUpdate) => {
const { total, count } = acc;
return { total: total + priceUpdate.price, count: count + 1 };
},
{ total: 0, count: 0 }
);
for await (const averageData of runningAverages) {
const averagePrice = averageData.total / averageData.count;
console.log(`O'zgaruvchan o'rtacha narx: ${averagePrice.toFixed(2)}`);
}
}
main();
Ushbu misolda `accumulator` funksiyasi narxlarning umumiy yig'indisini va olingan yangilanishlar sonini hisoblaydi. Yakuniy o'rtacha narx esa ushbu jamlangan qiymatlardan hisoblab chiqiladi. Bu turli global bozorlarda aksiya narxining real vaqtdagi ko'rinishini ta'minlaydi.
5. Veb-sayt trafikini global miqyosda tahlil qilish
Dunyo bo'ylab joylashgan serverlardan veb-saytga tashriflar haqidagi ma'lumotlar oqimini qabul qiladigan global veb-tahlil platformasini tasavvur qiling. Har bir ma'lumot nuqtasi veb-saytga tashrif buyurgan foydalanuvchini anglatadi. `scan` yordamida biz har bir mamlakat bo'yicha sahifalarni ko'rish tendentsiyasini real vaqtda tahlil qilishimiz mumkin. Aytaylik, ma'lumotlar shunday ko'rinishda: `{ country: "US", page: "homepage", timestamp: 1678886400 }`.
async function* generateWebsiteVisits() {
yield { country: 'US', page: 'homepage', timestamp: Date.now() };
yield { country: 'CA', page: 'product', timestamp: Date.now() };
yield { country: 'UK', page: 'blog', timestamp: Date.now() };
yield { country: 'US', page: 'product', timestamp: Date.now() };
}
async function main() {
const visitStream = generateWebsiteVisits();
const pageViewCounts = scan(
visitStream,
(acc, visit) => {
const { country } = visit;
const newAcc = { ...acc };
newAcc[country] = (newAcc[country] || 0) + 1;
return newAcc;
},
{}
);
for await (const counts of pageViewCounts) {
console.log('Mamlakatlar bo\'yicha sahifalarni ko\'rishlar soni:', counts);
}
}
main();
Bu yerda `accumulator` funksiyasi har bir mamlakat uchun hisoblagichni yangilaydi. Natijada yangi tashrif ma'lumotlari kelib tushganda har bir mamlakat uchun jamlanib borayotgan sahifalarni ko'rishlar soni ko'rsatiladi.
`scan` dan foydalanishning afzalliklari
`scan` yordamchisi asinxron ma'lumotlar oqimlari bilan ishlashda bir qancha afzalliklarni taqdim etadi:
- Deklarativ uslub: `scan` sizga akkumulyativ qayta ishlash mantig'ini deklarativ va ixcham tarzda ifodalashga imkon beradi, bu esa kodning o'qilishini va qo'llab-quvvatlanishini yaxshilaydi.
- Asinxron ishlov berish: U akkumulyator funksiyasi ichidagi asinxron operatsiyalarni muammosiz boshqaradi, bu uni I/O ga bog'liq vazifalarni o'z ichiga olgan murakkab stsenariylar uchun mos qiladi.
- Real vaqtda qayta ishlash: `scan` ma'lumotlar oqimlarini real vaqtda qayta ishlashga imkon beradi, bu sizga o'zgarishlarga ular sodir bo'lishi bilan munosabat bildirish imkonini beradi.
- Kompozitsionlik: Murakkab ma'lumotlarni qayta ishlash quvurlarini yaratish uchun uni boshqa asinxron iterator yordamchilari bilan osongina birlashtirish mumkin.
`scan` ni amalga oshirish (Agar u mavjud bo'lmasa)
Ba'zi kutubxonalar o'rnatilgan `scan` yordamchisini taqdim etsa-da, agar kerak bo'lsa, o'zingiz ham osongina amalga oshirishingiz mumkin. Mana oddiy amalga oshirish:
async function* scan(sourceIterator, accumulator, initialValue) {
let accumulatedValue = initialValue;
let first = true;
for await (const value of sourceIterator) {
if (first && initialValue === undefined) {
accumulatedValue = value;
first = false;
} else {
accumulatedValue = accumulator(accumulatedValue, value);
}
yield accumulatedValue;
}
}
Ushbu amalga oshirish manba iterator bo'ylab harakatlanadi va har bir qiymatga akkumulyator funksiyasini qo'llaydi, jamlangan natijani chiqaradi. U `initialValue` taqdim etilmagan holatni manba iteratordan birinchi qiymatni boshlang'ich qiymat sifatida ishlatish orqali boshqaradi.
`reduce` bilan taqqoslash
`scan` ni `reduce` dan farqlash muhimdir. Ikkalasi ham iteratorlar ustida ishlaydi va akkumulyator funksiyasidan foydalansa-da, ular o'z xatti-harakatlari va natijalari bilan farqlanadi.
- `scan` har bir iteratsiya uchun jamlangan qiymatni chiqaradi, bu esa jamlanishning o'zgaruvchan tarixini taqdim etadi.
- `reduce` iteratordagi barcha elementlarni qayta ishlagandan so'ng faqat yakuniy jamlangan qiymatni chiqaradi.
Shuning uchun, `scan` jamlanishning oraliq holatlarini kuzatib borish kerak bo'lgan stsenariylar uchun mos keladi, `reduce` esa faqat yakuniy natija kerak bo'lganda to'g'ri keladi.
Xatolarni Boshqarish
Asinxron iteratorlar va `scan` bilan ishlashda xatolarni to'g'ri boshqarish juda muhim. Xatolar iteratsiya jarayonida yoki akkumulyator funksiyasi ichida yuz berishi mumkin. Ushbu xatolarni ushlash va boshqarish uchun `try...catch` bloklaridan foydalanishingiz mumkin.
async function* generatePotentiallyFailingData() {
yield 1;
yield 2;
throw new Error('Nimadir noto\'g\'ri ketdi!');
yield 3;
}
async function main() {
const dataStream = generatePotentiallyFailingData();
try {
const accumulatedData = scan(dataStream, (acc, value) => acc + value, 0);
for await (const data of accumulatedData) {
console.log(data);
}
} catch (error) {
console.error('Xatolik yuz berdi:', error);
}
}
main();
Ushbu misolda `try...catch` bloki `generatePotentiallyFailingData` iteratori tomonidan yuzaga keltirilgan xatoni ushlaydi. Keyin siz xatoni mos ravishda boshqarishingiz mumkin, masalan, uni jurnalga yozish yoki operatsiyani qayta urinish.
Xulosa
`scan` yordamchisi JavaScript asinxron iteratorlarida asinxron akkumulyativ qayta ishlashni amalga oshirish uchun kuchli vositadir. U sizga murakkab ma'lumotlar transformatsiyalarini deklarativ va ixcham tarzda ifodalash, asinxron operatsiyalarni to'g'ri boshqarish va ma'lumotlar oqimlarini real vaqtda qayta ishlash imkonini beradi. Uning funksionalligi va qo'llanilish holatlarini tushunib, siz yanada mustahkam va samarali asinxron ilovalar yaratish uchun `scan` dan foydalanishingiz mumkin. O'zgaruvchan jami summalarni hisoblash, ma'lumotlarni massivlarga jamlash, tezlikni cheklovchilarni amalga oshirish yoki real vaqtda ma'lumotlarni yig'uvchilarni yaratish bo'ladimi, `scan` kodingizni soddalashtirishi va uning umumiy unumdorligini oshirishi mumkin. Asinxron ma'lumotlar oqimlarini qayta ishlash jarayonida oraliq jamlangan qiymatlarga kirish zarur bo'lganda, xatolarni boshqarishni unutmang va `reduce` o'rniga `scan` ni tanlang. RxJS kabi kutubxonalarni o'rganish reaktiv dasturlash paradigmalarida `scan` ni tushunishingizni va amaliy qo'llashingizni yanada kuchaytirishi mumkin.